/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.qudexapps.manager.services;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.igfay.jfig.JFigException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

//import uk.ljmu.metadata.schemas.SchemaInstanceContainer;
import uk.ljmu.metadata.utilities.Constants;
import uk.ljmu.metadata.utilities.MetadataUtilities;
import uk.ljmu.qudexapps.manager.domain.FilesModel;
import uk.ljmu.qudexapps.manager.domain.Relation;
import uk.ljmu.qudexapps.manager.utilities.GeneralUtilities;
import uk.ljmu.qudexapps.xmllib.exceptions.FoxmlException;
import uk.ljmu.qudexapps.xmllib.model.CollectionElement;
import uk.ljmu.qudexapps.xmllib.services.QudexFoxmlService;
import uk.ljmu.qudexapps.xmllib.services.QudexFoxmlServiceImpl;
import uk.ljmu.qudexapps.xmllib.utilities.IngestionResults;

@Service("spreadsheetService")
public class SpreadsheetServiceImpl implements SpreadsheetService {
    /**
     * 
     */
	@SuppressWarnings("unused")
	private static final long serialVersionUID = -1468747790352243151L;

    @SuppressWarnings("unused")
    private static Logger logger = Logger
	    .getLogger(SpreadsheetServiceImpl.class);
    @Autowired
    private FilesModel files;
    /**
     * ATTRIBUTES
     */

    transient private QudexFoxmlService ifz = null;

    private List<CollectionElement> treeList;

    private String fileType = "";

    @SuppressWarnings("unused")
    private String fileSeparator = System.getProperty("file.separator");

    private StringBuilder htmlSubTree = new StringBuilder();
    
    /**
     * This holds the Fedora PID of the root Qudex Collection with which we are
     * operating in the repository current process
     */
    private String workingQudexRootCollection;
    
    private String relationResults;

    private static String JQUERY_BIT;

    static {
	StringBuilder sb = new StringBuilder();
	sb.append("<script type=\"text/javascript\">\n");
	sb.append("	$(document).ready(function () {\n");
	sb.append("     $('ul.tree li:last-child').addClass('last');\n");
	sb.append("	});\n");
	sb.append("</script>\n");
	JQUERY_BIT = sb.toString();
    }

    public String getRelationResults() {
		return relationResults;
	}

	public void setRelationResults(String relationResults) {
		this.relationResults = relationResults;
	}

	/**
     * @param ifz
     *            the ifz to set
     */
    public void setIfz(QudexFoxmlService ifz) {
	this.ifz = ifz;
    }

    /**
     * @return the ifz
     */
    public QudexFoxmlService getIfz() {
	return ifz;
    }

    /**
     * Assigns the file type of the upload: CSV or Excel files
     * 
     * @param fileType
     */
    public void setFileType(String fileType) {
	this.fileType = fileType;
    }

    /**
     * Returns the type of the uploaded file
     * 
     * @return
     */
    public String getFileType() {
	return fileType;
    }

    public void setFiles(FilesModel files) {
	this.files = files;
    }

    public FilesModel getFiles() {
	return files;
    }

    public String getWorkingQudexRootCollection() {
		return workingQudexRootCollection;
	}

	public void setWorkingQudexRootCollection(String workingQudexRootCollection) {
		this.workingQudexRootCollection = workingQudexRootCollection;
	}

	public SpreadsheetServiceImpl() throws FoxmlException, JFigException {
	this.ifz = new QudexFoxmlServiceImpl(this.fileType, true);
	this.treeList = this.ifz.getQudexCollectionsTree();
	this.workingQudexRootCollection = "";
    }

    public FilesModel createFilesModel() {
	return new FilesModel();
    }

    public List<IngestionResults> ingestCollectionsSpreadsheet()
	    throws FoxmlException, IOException, Exception {

	this.ifz.setObjResults(new ArrayList<IngestionResults>());
	this.ifz.setQudexCollections(true);
	if (files.getSpreadsheet().getOriginalFilename() != "") {
	    //SchemaInstanceContainer container = new SchemaInstanceContainer();
	    ifz.setFileType(files.getSpreadsheet().getContentType());
	    ifz.setOriginalSpreadsheetName(files.getSpreadsheet()
		    .getOriginalFilename());
	    ifz.ingestQudexCollectionsSpreadsheet(this.cleanCollectionIds(files
		    .getCollectionsIds()), files.getSpreadsheet()
		    .getInputStream());
	    // Clean resources
	    //container.getListSchemaInstances().clear();
	}
	return ifz.getObjResults();
    }

    /**
     * Ingest a qudex file-level spreadsheet into the repository
     * 
     * @param path
     *            spreadsheet directory path
     * 
     * @return list with ingestion results
     */
    public List<IngestionResults> ingestFilesSpreadsheet()
	    throws FoxmlException, IOException, Exception {

	this.ifz.setObjResults(new ArrayList<IngestionResults>());
	this.ifz.setQudexCollections(false);
	if (files.getSpreadsheet().getOriginalFilename() != "") {
	    //SchemaInstanceContainer container = new SchemaInstanceContainer();
	    ifz.setFileType(files.getSpreadsheet().getContentType());
	    ifz.setOriginalSpreadsheetName(files.getSpreadsheet()
		    .getOriginalFilename());
	    // TODO CHECK IF COLLECTIONS PARAMETER IS NEEDED
	    ifz.ingestFileLevelSpreadsheet(this.cleanCollectionIds(this
		    .getFiles().getCollectionsIds()), files
		    .getModifiedSpreadsheet() != null ? files
		    .getModifiedSpreadsheet() : files.getSpreadsheet()
		    .getInputStream());
	    // ifz.ingestFileLevelSpreadsheet(files.getSpreadsheet().getInputStream());
	    // Clean resources
	    //container.getListSchemaInstances().clear();
	}
	return ifz.getObjResults();
    }

    @SuppressWarnings("unused")
    private String returnFileExtension(String contentType) {
	if (contentType.equals("text/csv"))
	    return ".csv";
	else if (contentType.equalsIgnoreCase("application/vnd.ms-excel")
		|| contentType.equalsIgnoreCase("application/ms-excel"))
	    return ".xsl";
	else
	    return "";
    }

    public List<IngestionResults> getResults() {
	return this.ifz.getObjResults();
    }

    // PRIVATE FUNCTIONS
    private List<String> cleanCollectionIds(String collectionList) {
	String[] listIds = collectionList.split(Constants.LIST_SEP);
	int i = 0;
	if (listIds.length > 0) {
	    for (String str : listIds) {
		if (str.contains(MetadataUtilities.OBJECTID))
		    listIds[i] = str.replace(MetadataUtilities.OBJECTID, "");
		i++;
	    }
	    return GeneralUtilities.removeElements(listIds,
		    uk.ljmu.qudexapps.xmllib.utilities.Constants.NOCOLLECTION);
	}

	return new ArrayList<String>();
    }

    public String getProgress() {
	return this.ifz.getProgressStatus();
    }

    public void setQudexCollections(boolean qudexCollections) {
	this.ifz.setQudexCollections(qudexCollections);

    }

    public List<String> getQudexCollections() {
	return this.ifz.getQudexCollections(this.getWorkingQudexRootCollection());
    }

    public List<CollectionElement> getQudexCollectionsTree() {
	return this.treeList;
    }

    // Print Subtree
    public List<String> printSubtree(String collectionId) {
	cleanDisplay();
	List<String> auxList = new ArrayList<String>();
	if (collectionId.equals("NULLVALUE")) {
	    this.htmlSubTree.delete(0, this.htmlSubTree.length());
	    auxList.add("");
	    auxList.add("");
	    return auxList;
	}
	// Find Root collection Node of the actual node
	String initialNode = this.ifz.getQudexRootParent(collectionId.replace(
		MetadataUtilities.OBJECTID, ""));
	//CollectionElement auxNode = CollectionElement.findElement(collectionId
	//	.replace(MetadataUtilities.OBJECTID, ""), this.treeList);
	//String replaceId = "";
	// AMG - trick to avoid printing the child node twice since it has
	// relation to the immediate parent and also to its root collection
	//if (auxNode.getImmediateParents().size() > 1) {
	//    for (String parent : auxNode.getImmediateParents()) {
	//	if (parent.equals(initialNode.replace(
	//		MetadataUtilities.OBJECTID, ""))) {
	//	    replaceId = collectionId.replace(
	//		    MetadataUtilities.OBJECTID, "");
	//	    break;
	//	}
	//    }
	//}
	if (initialNode != null) {
	    CollectionElement collectionNode = CollectionElement.findElement(
		    initialNode.replace(MetadataUtilities.OBJECTID, ""),
		    this.treeList);
	    if (collectionNode == null) {
		    this.htmlSubTree
			    .append("<div id=\"printTree\" class=\"example\">\n");
		    this.htmlSubTree
		    .append("ERROR TREE NULL");
		    this.htmlSubTree.append("</div>\n");
		    auxList.add(this.htmlSubTree.toString());
		    auxList.add(collectionId);
		    this.htmlSubTree.delete(0, this.htmlSubTree.length());
		    return auxList;
	    }
	    this.htmlSubTree.delete(0, this.htmlSubTree.length());
	    this.htmlSubTree.append(JQUERY_BIT);
	    this.htmlSubTree.append("<h3>Selected collection navigation</h3>");
	    this.htmlSubTree
		    .append("<p>The selected collection presents the following structure in the repository:</p>");
	    this.htmlSubTree
		    .append("<div id=\"printTree\" class=\"example\">\n");
	    this.htmlSubTree.append("<ul class=\"tree\" id=\"tree\">\n");
	    //traverseTree(collectionNode, 1, replaceId, collectionId.replace(
	    //	    MetadataUtilities.OBJECTID, ""));
	    traverseTree(collectionNode, 1, collectionId.replace(
	    	MetadataUtilities.OBJECTID, ""));
	    this.htmlSubTree.append("</ul>\n");
	    this.htmlSubTree.append("</div>\n");
	} else {
	    CollectionElement collectionNode = CollectionElement.findElement(
		    collectionId.replace(MetadataUtilities.OBJECTID, ""),
		    this.treeList);
	    // Root Collection
	    this.htmlSubTree.delete(0, this.htmlSubTree.length());
	    this.htmlSubTree.append(JQUERY_BIT);
	    this.htmlSubTree.append("<h3>Selected collection navigation</h3>");
	    this.htmlSubTree
		    .append("<p>The selected collection presents the following structure in the repository:</p>");
	    this.htmlSubTree
		    .append("<div id=\"printTree\" class=\"example\">\n");
	    this.htmlSubTree.append("<p>The collection with title: &quot;<b>"
		    + collectionNode.getCollectionTitle()
		    + "</b>&quot; is a Collection Container</p>");
	    this.htmlSubTree.append("</div>\n");
	}
	auxList.add(this.htmlSubTree.toString());
	auxList.add(collectionId);
	this.htmlSubTree.delete(0, this.htmlSubTree.length());
	return auxList;
    }

    /*private void traverseTree(CollectionElement currentElement, int depth,
	    String excludeNodeId, String highlight) {
	// int j;
	String skip = "";
	// Opening LI
	if (currentElement.getCollectionId().equals(highlight))
	    this.htmlSubTree.append("<li class=\"highlight\">\n");
	else
	    this.htmlSubTree.append("<li>\n");
	this.htmlSubTree.append(currentElement.getCollectionTitle());
	this.htmlSubTree.append(" (Repository identifier: ");
	this.htmlSubTree.append(currentElement.getCollectionId());
	this.htmlSubTree.append(")");
	this.htmlSubTree.append("\n");
	// Traverse the tree
	int i = 0;
	String currentElementChild = "";
	if (excludeNodeId != "") {
	    for (String child : currentElement.getImmediateChildren()) {
		if (child.equals(excludeNodeId)) {
		    skip = excludeNodeId;
		    currentElement.getImmediateChildren().add(0, skip);
		    currentElement.getImmediateChildren().remove(child);		    
		    excludeNodeId = "";		    
		    break;
		}
	    }
	}
	if (currentElement.getImmediateChildren().size() > 0) {
	    currentElementChild = currentElement.getImmediateChildren().get(i);
	    // SUB UL
	    this.htmlSubTree.append("<ul>\n");
	    while (i < currentElement.getImmediateChildren().size()) {
		if (currentElementChild.equals(skip)) {
		    i++;
		    if (i < currentElement.getImmediateChildren().size())
			currentElementChild = currentElement.getImmediateChildren().get(i);
		    continue;
		}
		// Recursively traverse the tree structure of the child node
		traverseTree(CollectionElement.findElement(currentElementChild,
			this.treeList), depth + 1, excludeNodeId, highlight);
		i++;
		if (i < currentElement.getImmediateChildren().size())
		    currentElementChild = currentElement.getImmediateChildren()
			    .get(i);
	    }
	    this.htmlSubTree.append("</ul>\n"); // CLOSE SUB UL
	}
	this.htmlSubTree.append("</li>\n");
    }*/
    private void traverseTree(CollectionElement currentElement, int depth,
	    String highlight) {
	if (!currentElement.isDisplayed()) {
	    currentElement.setDisplayed(true);
	    // Opening LI
	    if (currentElement.getCollectionId().equals(highlight))
		this.htmlSubTree.append("<li class=\"highlight\">\n");
	    else
		this.htmlSubTree.append("<li>\n");
	    this.htmlSubTree.append(currentElement.getCollectionTitle());
	    this.htmlSubTree.append(" (Repository identifier: ");
	    this.htmlSubTree.append(currentElement.getCollectionId());
	    this.htmlSubTree.append(")");
	    this.htmlSubTree.append("\n");
	    // Traverse the tree
	    int i = 0;
	    String currentElementChild = "";

	    if (currentElement.getImmediateChildren().size() > 0) {
		currentElementChild = currentElement.getImmediateChildren()
			.get(i);
		// SUB UL
		this.htmlSubTree.append("<ul>\n");
		while (i < currentElement.getImmediateChildren().size()) {
		    // Recursively traverse the tree structure of the child node
		    traverseTree(CollectionElement.findElement(
			    currentElementChild, this.treeList), depth + 1,
			    highlight);
		    i++;
		    if (i < currentElement.getImmediateChildren().size())
			currentElementChild = currentElement
				.getImmediateChildren().get(i);
		}
		this.htmlSubTree.append("</ul>\n"); // CLOSE SUB UL
	    }
	    this.htmlSubTree.append("</li>\n");
	}
    }
    
    public void cleanDisplay() {
	for (CollectionElement elem : treeList) {
	    elem.setDisplayed(false);
	}
    }
    
    public void regenerateTree() {
	this.treeList = this.ifz.getQudexCollectionsTree();
    }

    public void setOriginalSpreadsheetPath(String path) {
	this.ifz.setOriginalSpreadsheetPath(path);
    }

    public void destroy() {
	if (this.ifz != null) {
	    this.ifz.cleanFullInstance();
	}
    }

    public void init() {

    }

	@Override
	public List<String> getQudexRootCollections() {
		return this.ifz.getQudexRootCollections();
	}

	@Override
	public List<String> getQudexInstanceCollections() {
		return this.ifz.getQudexInstanceCollections();
	}

	@Override
	public List<String> getQudexCollectionDocuments() {
		return this.ifz.getQudexCollectionDocuments(this.getWorkingQudexRootCollection());
	}

	@Override
	public void addFedoraRelation(Relation relationObject) {
		// TODO Auto-generated method stub
		String results = this.ifz.getFedoraObject()
				.addRelationship(
						relationObject.getSubjectRelation().replace(
								"info:fedora/", ""),
						relationObject.getSubjectRelation(),
						"http://example.org/qudex/"
								+ relationObject.getRelationName(),
						relationObject.getObjectRelation(), "false", "");
		if (results == null) {
			System.out.println("ERROR REL NOT CREATED");
			this.setRelationResults("Relationship not created! Error(s) ocurred. Check Application log.");
		} else {
			String title = this.ifz.getTitle(relationObject.getSubjectRelation());
			String title2 = this.ifz.getTitle(relationObject.getObjectRelation());
			String sms = "<p>Relationship added:</p>" + "<p> Relation subject: "+ title + "</p><p>Relation name: " + relationObject.getRelationName() + "</p><p>Relation object: " + title2 + "</p>";
			this.setRelationResults(sms);
		}
	}
}
